import {
  DeepPartial,
  FindManyOptions,
  FindOneOptions,
  FindOptionsWhere,
  ObjectId,
  Repository,
  SaveOptions,
  UpdateResult,
} from 'typeorm'
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity'
import { UpsertOptions } from 'typeorm/repository/UpsertOptions'

export class BaseService<T, K extends keyof T = never> {
  constructor(
    protected readonly repository: Repository<T>,
    private readonly primaryId?: K, // 主键字段名, xxxId
  ) {}

  /** 创建：排除主键和时间字段 */
  create(entityLike: DeepPartial<T>): T {
    return this.repository.create(entityLike)
  }

  /** 更新：排除主键和时间字段 */
  async save(entity: DeepPartial<T>, options?: SaveOptions): Promise<T> {
    return await this.repository.save(entity, options)
  }

  async findOne(options?: FindOneOptions<T>) {
    return await this.repository.findOne(options)
  }

  async find(options?: FindManyOptions<T>) {
    return await this.repository.find(options)
  }

  async findPage({
    pageNo = 1,
    pageSize = 10,
    where,
    select,
    relations,
  }: {
    pageNo?: number
    pageSize?: number
    select?: FindOneOptions<T>['select']
    where: FindOptionsWhere<T>
    relations?: FindOneOptions<T>['relations']
  }): Promise<{ total: number, pageNo: number, pageSize: number, list: T[] }> {
    const [list, total] = await this.repository.findAndCount({
      where,
      select,
      relations,
      skip: (pageNo - 1) * pageSize,
      take: pageSize,
    })
    return { list, total, pageNo, pageSize }
  }

  async findAll(options?: FindManyOptions<T>) {
    return await this.repository.find(options)
  }

  async update(
    criteria:
      | string
      | string[]
      | number
      | number[]
      | Date
      | Date[]
      | ObjectId
      | ObjectId[]
      | FindOptionsWhere<T>
      | FindOptionsWhere<T>[],
    partialEntity: QueryDeepPartialEntity<T>,
  ): Promise<UpdateResult> {
    return await this.repository.update(criteria, partialEntity)
  }
  // async update(id: number, data: Partial<Omit<T, K | 'createdAt' | 'updatedAt'>>) {
  //   await this.repository.update(id, {
  //     ...data,
  //     [this.primaryId]: undefined,
  //   })
  // }

  upsert(
    entityOrEntities: QueryDeepPartialEntity<T> | QueryDeepPartialEntity<T>[],
    conflictPathsOrOptions: string[] | UpsertOptions<T>,
  ) {
    this.repository.upsert(entityOrEntities, conflictPathsOrOptions)
  }

  async delete(id: number) {
    return await this.repository.delete(id)
  }
}
